#

import logging
import os
import sys
from os import path
import collections

LOGGER = logging.getLogger("wglmkapplayer")


DEBUG = bool(os.environ.get('DEBUG', 'no') != 'no')
    

# Dumper
#
class Dumper :


    # __init__
    #
    def __init__ (self, f_out) :
        self.f_out = f_out
        self.tab_stack = [0]
        self.tab_string = ''
        self.bol = True


    # tab
    #
    def tab (self) :
        self.tab_stack.append(self.tab_stack[-1] + 1)
        self.tab_string = '  ' * self.tab_stack[-1]
        

    # untab
    #
    def untab (self) :
        assert len(self.tab_stack) >= 1
        self.tab_stack.pop()
        self.tab_string = '  ' * self.tab_stack[-1]


    # newline
    #
    def newline (self) :
        self.f_out.write("\n")
        self.bol = True


    # write
    #
    def write (self, text) :
        assert '\n' not in text, text
        assert '\t' not in text, text
        assert '\b' not in text, text
        assert '\v' not in text, text
        if self.bol :
            self.f_out.write(self.tab_string)
            self.bol = False
        self.f_out.write(text)


# WNode
#
class WNode :

    def dump (self, dumper) :
        raise NotImplementedError


# WCtrl
#
class WCtrl (WNode) :

    def dump (self, dumper) :
        raise NotImplementedError


# WCtrlNL
#
class WCtrlNL (WCtrl) :

    def dump (self, dumper) :
        dumper.newline()


# Control chars
#
WNL = WCtrlNL()


# WText
#
class WText (WNode) :

    def __init__ (self, text) :
        self.text = text

    def dump (self, dumper) :
        dumper.write(self.text)


# WBlock
#
class WBlock (WNode) :

    def __init__ (self, parent, indent=False, braces=False) :
        assert braces in (False, True, 'class'), braces
        self.indent = indent
        self.braces = braces
        self.children = []
        # self.parent = parent
        if parent :
            parent.children.append(self)

    def write (self, *text) :
        for t in text :
            # [todo]
            assert '\t' not in t, t
            assert '\b' not in t, t
            while True :
                c = t.find('\n')
                if c >= 0 :
                    t1 = t[:c]
                    if t1 :
                        self.children.append(WText(t1))
                    self.children.append(WNL)
                    t = t[c+1:]
                else :
                    if t :
                        self.children.append(WText(t))
                    break

    def writeln (self, *lines) :
        for l in lines :
            self.write(l)
            self.children.append(WNL)

    def section (self, **kwargs) :
        sec = WSection(self, **kwargs)
        return sec

    def dump (self, dumper) :
        if self.braces :
            dumper.write('{')
            dumper.newline()
        if self.indent :
            dumper.tab()
        for child in self.children :
            child.dump(dumper)
        if self.indent :
            dumper.untab()
        if self.braces :
            dumper.write('}')
            if self.braces == 'class' :
                dumper.write(';')
            dumper.newline()
            

# WSection
#
class WSection (WNode) :

    def __init__ (self, parent, indent_body=False, **kwargs) :
        self.head = WBlock(parent)
        self.body = WBlock(parent, indent=indent_body, **kwargs)
        self.foot = WBlock(parent)

    def dump (self, dumper) :
        self.head.dump(dumper)
        self.body.dump(dumper)
        self.foot.dump(dumper)


# WFile
#
class WFile (WNode) :

    def __init__ (self, filename) :
        self.filename = filename
        self.root = WSection(None)

    def guard_section (self) :
        basename = path.basename(self.filename)
        guard = '_' + basename.upper().replace('.', '_').replace('-', '_')
        s_guard = self.root.body.section()
        s_guard.head.writeln(f"#ifndef {guard}",
                             f"#define {guard}")
        s_guard.foot.writeln(f"#endif // ifdef {guard}")
        return s_guard

    def dump (self, dumper) :
        LOGGER.debug(f"WFile.dump({self.filename})")
        basename = path.basename(self.filename)
        dumper.write(f"// {basename} - AUTOGENERATED")
        dumper.newline()
        self.root.dump(dumper)


# Writer
#
class Writer :


    # __init__
    #
    def __init__ (self, basename) :
        self.basename = basename
        self.files = collections.OrderedDict()


    # add_file
    #
    def add_file (self, filename) :
        filepath = path.realpath(filename)
        assert filepath not in self.files, filepath
        fnode = WFile(filepath)
        self.files[filepath] = fnode
        return fnode
    

    # write
    #
    def write (self) :
        raise NotImplementedError


    # dump
    #
    def dump (self) :
        LOGGER.debug("WRITER DUMP")
        for fpath, wfile in self.files.items() :
            LOGGER.debug(f"FILE: {fpath}")
            tmpfile = fpath + '.tmp'
            with open(tmpfile, 'w', encoding='ascii') as f_out :
                dumper = Dumper(f_out)
                wfile.dump(dumper)
            os.rename(tmpfile, fpath)
            # debug
            if DEBUG :
                LOGGER.debug(f"** {fpath}  **")
                with open(fpath, 'r', encoding='ascii') as f_out :
                    lno = 1
                    for line in f_out :
                        sys.stderr.write(f"{lno:>4} {line}")
                        lno += 1
                LOGGER.debug("********************************")
